import pygtk
pygtk.require('2.0')
import gtk
import lessonMode
import random
import sound
import os
import pygame
import time

TYPING_ROOT = os.path.dirname(os.path.abspath(__file__))
while(os.path.isfile(TYPING_ROOT)):
    TYPING_ROOT = os.path.dirname(TYPING_ROOT)
IMAGE_SOURCE = TYPING_ROOT + os.sep + "images" + os.sep + "assessment" 

class AssessmentMode(lessonMode.LessonGame):

    #Setup the Assessment mode to run a review.
    def __init__(self, window, lessons, useSounds, useImages, useWords):
        #Build most of the window, which is the same as lessnMode
        lessonMode.LessonGame.__init__(self, window, lessons, False)
        self.initialrun = False
        
        #Keep variables to know what we're quizzing with.
        self.useSounds = useSounds
        self.useImages = useImages
        self.useWords = useWords

        #Setup Variables to keep track of keystrokes        
        self.strokes = 0
        self.correctStrokes = 0

        #Setup view to hold the displayed accuracy states.
        accuracyWindow = gtk.Frame()
        accuracyView = gtk.TextView()
        
        #As the view is to be uneditable, hid the cursor.  Center the text, too        
        accuracyView.set_cursor_visible(False)
        accuracyView.set_justification(gtk.JUSTIFY_CENTER)
        accuracyView.set_editable(False)

        self.accuracyBuffer = accuracyView.get_buffer()
        accuracyWindow.add(accuracyView)
        accuracyWindow.show()
        accuracyView.show()

        #Set starting text blank        
        self.accuracyBuffer.set_text("")

        #Create tag for size, and attach the window
        sizetag = gtk.TextTag("size")
        sizetag.set_property("size-points", 20)
        self.accuracyBuffer.get_tag_table().add(sizetag)
        self.membox.hide()
        self.table.attach(accuracyWindow, 3, 7, 0, 1)

    #We display help in two cases:
    #If there's sounds quizzed on, but nothing else, show images.
    #If there are images but not words, replay the sounds
    def helpdisplay(self, widget):
        if(self.useSounds and not self.useImages and not self.useWords):
            self.showImage(self.currentWord, False)
        elif(self.useImages and not self.useWords):
            self.playSound()
        return True

    #Plays the sound for the current word
    def playSound(self):
        #Make sure no other sound is playing
        #while(pygame.mixer.get_busy()):
        #    True
            
        #Load up the sound and play it.
        soundPlay = sound.Sound()
        soundPlay.setSound(TYPING_ROOT + os.sep + "sounds" + os.sep + self.currentWord.lower())
        soundPlay.run()

    #Displays the image for the current word.
    def showImage(self, imageName, showWord):
        self.lessonChosen = self.lessonsFrom[self.wordNum - 1]
        lessonMode.LessonGame.showImage(self, imageName, showWord)

    #Loads up the lesson.
    #Overloads the equivalent method in lessonMode.
    def startLesson(self, lessonName):
        #Choose up to twenty words, instead of loading a lesson
        self.lessonWords = self.getTwentyWords()
        self.wordNum = 0
        self.currentWord = ""
        self.playWord = ""
        self.goThree = False
        return True

    #Chooses up to twenty words to quiz us on.  If there
    #are fewer than 20 words in memory, then we take as
    #many as there are.  Also sets numWords to the number
    #we load (which, if it is 0, will later prevent the
    #lesson from running
    def getTwentyWords(self):
        self.numWords = 15
        self.toReturn = []
        self.lessonsFrom = []
        #If there are fewer than or exactly 20 words,
        #just load them all up.
        if len(self.memoryLabels) <= 15:
            self.numWords = len(self.memoryLabels)
            for x in self.memoryLabels.keys():
                self.toReturn.append(self.memoryLabels[x].get_text())
                self.lessonsFrom.append(x[1])
        #Otherwise, use a parallel list, randomly populated
        #to re-order the words and then take the first 20.
        else:
            keys = self.memoryLabels.keys()
            sortAgainst = []
            for x in range(0,len(keys)):
                sortAgainst.append(random.randint(0,1000))
            keys = (self.jointSort(keys,sortAgainst))[0]
            for x in range(0,15):
                self.toReturn.append(self.memoryLabels[keys[x]].get_text())
                self.lessonsFrom.append((keys[x])[1])
        return self.toReturn  

    #Basic Mergesort merge function, for two pairs of lists.
    #Sorts on the second list of each pair, and resorts
    #the first with the same operations.
    def merge(self, leftPair, rightPair):
        leftWith = leftPair[0]
        leftBy = leftPair[1]
        rightWith = rightPair[0]
        rightBy = rightPair[1]
        leftIndex = 0;
        rightIndex = 0;
        resultWith = []
        resultBy = []
        while (leftIndex < len(leftBy) and rightIndex < len(rightBy)):
            if(leftBy[leftIndex] < rightBy[rightIndex]):
                resultWith.append(leftWith[leftIndex])
                resultBy.append(leftBy[leftIndex])
                leftIndex = leftIndex+1
            else:
                resultWith.append(rightWith[rightIndex])
                resultBy.append(rightBy[rightIndex])
                rightIndex = rightIndex+1
        while(leftIndex < len(leftBy)):
            resultWith.append(leftWith[leftIndex])
            resultBy.append(leftBy[leftIndex])
            leftIndex = leftIndex+1
        while(rightIndex < len(rightBy)):
            resultWith.append(rightWith[rightIndex])
            resultBy.append(rightBy[rightIndex])
            rightIndex = rightIndex+1
        return (resultWith, resultBy)

    #Basic mergeSort recursive algorithm.
    #Modifies two lists, sorting sortBy
    #and doing all the same operations
    #to sortWith
    def jointSort(self, sortWith, sortBy):
        if(len(sortBy) <= 1):
            return (sortWith, sortBy)
        else:
            mid = len(sortBy) / 2
            left = self.jointSort(sortWith[0:mid], sortBy[0:mid])
            right = self.jointSort(sortWith[mid:], sortBy[mid:])
            return self.merge(left, right)

    #Prevent any sort of memory returns for the buttons.
    #They shouldn't be visible, but this prevents them
    #from doing anything if they are.
    def memoryCallback(self, widget, event, wordNum):
        return False

    #We don't want to add any word used to this function,
    #So if we are somehow going to, this guarentees we won't.
    def memContains(self, word):
        return True

    #Determine if the key pressed was the correct one.
    def isRight(self, widget, event):
        #Increase strokes by one.
        self.strokes = self.strokes + 1

        #Use the super-classes isRight to do many things (like play sounds)        
        lessonMode.LessonGame.isRight(self,widget, event)

        #Also, update the accuracy display
        percent = int(100*self.correctStrokes/self.strokes)
        outputtext = "  %i/%i, %i" % (self.correctStrokes, self.strokes, percent)
        outputtext = outputtext+ "%"

        #Make sure the picture works, and loads up.
        try:
            file_check = file(IMAGE_SOURCE + os.sep + "target.gif")
        except IOError, error:
            print error
        else:
            picsize = 30
            pixbuf = gtk.gdk.pixbuf_new_from_file(IMAGE_SOURCE + os.sep + "target.gif").scale_simple(picsize, picsize, gtk.gdk.INTERP_BILINEAR)
            self.accuracyBuffer.set_text(outputtext)
            self.accuracyBuffer.insert_pixbuf(self.accuracyBuffer.get_start_iter(), pixbuf)

        self.accuracyBuffer.apply_tag(self.accuracyBuffer.get_tag_table().lookup("size"), self.accuracyBuffer.get_start_iter(), self.accuracyBuffer.get_end_iter())
        return True

    #Performs the relevant operations if this was the correct keystroke.
    #Is called from the super-classes "isRight" function
    def doRightStroke(self, widget, event):
        #Since this was the right stroke, increase correct strokes.
        self.correctStrokes = self.correctStrokes+1
        #Once again, use the super-classes to make sure things are done right
        lessonMode.LessonGame.doRightStroke(self, widget, event)

        #If we aren't quizzing with words, no changes would occur to show progress.
        #So, we output what the user has typed so far.
        if(not self.useWords):
            toOutput = self.inputString[0:self.currChar]
            self.setup_next_word(toOutput)
            self.textbuffer.apply_tag(self.sizetag, self.textbuffer.get_start_iter(), self.textbuffer.get_end_iter())
        return True

    #Overrides the super-classes getNextWord, to make sure
    #we never do a three-word review (irrelevant for assessment mode)
    def getNextWord(self):
        toReturn = lessonMode.LessonGame.getNextWord(self)
        self.goThree = False
        return toReturn

    #Overrides the super-class displayReward function,
    #to compute the final words per minute and accuracy
    #Ratings, and display them instead of the lesson completed
    def displayReward(self):
        self.assessmentresult = gtk.TextView()    
        self.assessmentresult.set_cursor_visible(False)
        self.assessmentresult.set_justification(gtk.JUSTIFY_CENTER)
        self.assessmentresult.set_editable(False)

        self.rewardlessonframe.add(self.assessmentresult)
        self.rewardlessonframe.show()
        textbuffer = self.assessmentresult.get_buffer()

        sizetag = gtk.TextTag("size")
        sizetag.set_property("size-points", 24)
        self.assessmentresult.get_buffer().get_tag_table().add(sizetag)
        
        percent = int(100*self.correctStrokes/self.strokes)
        outputtext = "\n%i/%i, %i" % (self.correctStrokes, self.strokes, percent)
        outputtext = outputtext + "%\nWPM:\n"
        WPM = int(self.wordNum * 60 / self.totalTime)
        outputtext = outputtext + "%i" % (WPM)

        try:
            file_check = file(IMAGE_SOURCE + os.sep + "target.gif")
        except IOError, error:
            print error
        else:
            picsize = 30
            pixbuf = gtk.gdk.pixbuf_new_from_file(IMAGE_SOURCE + os.sep + "target.gif").scale_simple(picsize, picsize, gtk.gdk.INTERP_BILINEAR)
            textbuffer.set_text(outputtext)
            textbuffer.insert_pixbuf(textbuffer.get_start_iter(), pixbuf)
            
        
            textbuffer.apply_tag(sizetag,textbuffer.get_start_iter(), textbuffer.get_end_iter())
    
            self.table.hide()        
            self.assessmentresult.show()
            self.rewardscreen.show()
            self.container.show()
        
            soundPlay = sound.Sound()
            soundPlay.setSound(TYPING_ROOT + os.sep + "sounds" + os.sep + "reward" + os.sep + "cheer4") 
            soundPlay.run()
        
            while gtk.events_pending():
                gtk.main_iteration(False)
            time.sleep(5)

    #Main function of assessment mode.  Overrides the super-class
    #runGame function.  Is mostly similar to the super-class
    #But there are some output/sound changes
    def runGame(self,lessonLetter):
        #Make sure we aren't expecting a dipthong
        self.dip_waiting = False
        self.dip_letter = ""

        #Let the keyboard trigger get signals        
        self.handler_unblock(self.keyboard_trigger)
        
        #Grab focus, so all widget events start here
        self.grab_add()

        #Setup other program variables.        
        self.quit = False
        self.abort = False
        self.lessonChosen = lessonLetter
        
        #Call to setup the assessment.
        #Will evaluate to false only if there are no words to quiz on
        if(not self.startLesson(self.lessonChosen)):
            print "Error!  Invalid Lesson"
            self.grab_remove()
            return False
        elif(self.numWords <= 0):
            print "No words, can't run assessment mode!"
            self.grab_remove()
            return False

        #Clear away all memory elements
        keys = self.memoryLabels.keys()
        for x in keys:
            self.memoryLabels[x].get_parent().hide()

        #Connect event for the closing of a window
        self.deleter = self.win.connect("delete_event", self.delete_event)

        #Get the current time, but also keep it as waiting because the user can't type yet.
        startTime = int(time.time())
        waitStartTime = int(time.time())
        waitingTime = 0

        #Keep going until we run out of words to quiz on
        while(self.getNextQuiz()):
            #Display the next query string, if using words.  Otherwise, display ""
            if(self.useWords):
                self.setup_next_word(self.inputString)
            else:
                self.setup_next_word("")

            #Show the word's image if using images.
            if(self.useImages):                
                self.showImage(self.currentWord,False)

            #Play the word's sound if using sounds            
            if(self.useSounds):
                self.playSound()
                
            #Make all the text big, so its readable.  Underline the current character, and highlight it on the keyboard
            self.textbuffer.apply_tag(self.sizetag, self.textbuffer.get_start_iter(), self.textbuffer.get_end_iter())
            self.tag_curr_char(self.undertag, True)
            self.highlight_curr_letter()

            #Start up and wait for the keyboard.  Begin counting time now, so ignore all time between start and now.
            waitingTime = waitingTime + int(time.time())-waitStartTime
            gtk.main()
            #The user finished a word, so begin waiting again
            #(can't get user input, since they don't know what to type)
            waitStartTime = int(time.time())
            if(self.abort):
                #Stop grabbing focus, so widget events will start as normal
                self.grab_remove()
                if(not self.quit):
                    self.handler_block(self.keyboard_trigger)
                return self.quit
            
            #Advance the progress.  Make sure we don't try to give more than 1.0 to the progress bar.
            if(self.progressBar.get_fraction() + (1.0 / self.numWords) < 1.0):
                self.progressBar.set_fraction(self.progressBar.get_fraction() + (1.0 / self.numWords))
            else:
                self.progressBar.set_fraction(1.0)
            #Claar away the current image, regardless of if there is one.
            self.imagetext.get_buffer().set_text("")

        #Compute one last time how much we waiting
        #(as we'll be pausing between when the user finishes the last word and now)
        waitingTime = waitingTime + int(time.time())-waitStartTime
        endTime = int(time.time())
        
        #The total time we take is the difference between now and the start time, less the amount of time we waited.
        self.totalTime = endTime-startTime-waitingTime
        
        #De-link the keyboard from input
        self.handler_block(self.keyboard_trigger)

        self.displayReward()
        
        #Disconnect the delete event, so things work correctly above.        
        self.win.disconnect(self.deleter)

        #Stop grabbing focus, so widget events will start as normal        
        self.grab_remove()
        return False

    
